package com.sgidi.einvoice.service;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.sgidi.einvoice.models.Invoice;
import org.apache.commons.codec.binary.Base64;
import org.apache.fontbox.ttf.CmapTable;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class PdfQrExtractor {
    public static Invoice extract(File file) throws IOException {
        String outputDir = "/"; // 设置为根目录

        PDDocument document = PDDocument.load(file);
        try {
            Result finalRet = null;
            for (PDPage page : document.getPages()) {
                PDResources pdResources = page.getResources();
                finalRet = processXObject(pdResources);
            }
            if (finalRet == null) return null;
            String[] arr = finalRet.getText().split(",");
            if (arr.length < 6) return null;
            Invoice invoice = parseFromArray(arr);
            invoice.setBase64Png(getBase64Image(document));
            SetTotalAmountForOldInvoice(document, invoice);
            document.close();
            return invoice;
        } catch (Exception e) {
            document.close();
            return null;
        }
    }

    private static Result processXObject(PDResources pdResources) throws IOException {
        for (COSName cosName : pdResources.getXObjectNames()) {
            PDXObject xObject = pdResources.getXObject(cosName);

            // 如果是图片，尝试解码二维码
            if (xObject instanceof PDImageXObject) {
                PDImageXObject image = (PDImageXObject) xObject;
                Result ret = decodeQRCode(image.getImage());
                if (ret != null) {
                    return ret;
                }
            }

            // 如果是表单 XObject，递归检查
            if (xObject instanceof PDFormXObject) {
                PDFormXObject form = (PDFormXObject) xObject;
                Result ret = processXObject(form.getResources());
                if (ret != null) {
                    return ret; // 找到二维码后返回
                }
            }
        }
        return null;
    }
    /*从字符串数组里按照固定的排列给Invoice赋值*/
    private static Invoice parseFromArray(String[] arr) {
        // 01,31,,23312000000149321598,750.00,20231202,,5038 新发票
        // 01,10,050002100311,78881306,317.81,20230505,44035891270363352468,BC4B, 老发票
        //CZ-EI-00,1.1.0,00010124,0017979500,130402,20241022,2000.00
        //第二位31：电子专用，32：电子普通
        String type = "";
        if(arr[1].equals("31"))
            type = "电子发票(增值税专用发票)";
        else if(arr[1].equals("32"))
            type = "电子发票(普通发票)";
        else if(arr[1].equals("51"))
            type = "电子发票（铁路电子客票）";
        else if(arr[1].equals("10"))
            type = "增值税电子普通发票";
        else if(arr[1].equals("11"))
            type = "增值税电子专用发票";
        Invoice invoice = new Invoice();
        invoice.setTitle(type);
        invoice.setCode(arr[2].trim());
        invoice.setNumber(arr[3].trim());

        if(arr[0].contains("CZ")){
            invoice.setTotalAmount(new BigDecimal(arr[6]));
        }
        else {
            double value = new Double(arr[4]);
            if (arr[2] != null && !arr[2].trim().isEmpty()) {
                BigDecimal d = new BigDecimal(value + value * 0.13);
                invoice.setTotalAmount(d.setScale(2, RoundingMode.HALF_UP));
            } else {
                invoice.setTotalAmount(new BigDecimal(arr[4]));
            }
        }
        return invoice;
    }

    private static Result decodeQRCode(BufferedImage bufferedImage) {
        BinaryBitmap binaryBitmap=null;
        try {

            bufferedImage = addWhiteBorder(bufferedImage,5);
            // Optional: Convert to grayscale
            bufferedImage = convertToBlackAndWhite(bufferedImage);

            // Optional: Resize image if it's too large or too small
            // Optional: Resize image if it's too large or too small
            if (bufferedImage.getWidth() < 400 || bufferedImage.getHeight() < 400) {
                bufferedImage = resize(bufferedImage, 500, 500);
            }
            //String imagePath = "c:/testfile/" + "image_ "  + UUID.randomUUID() + ".png";
            //ImageIO.write(bufferedImage, "png", new File(imagePath));
            LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));

            Reader reader = new MultiFormatReader();

            // 设置解码参数
            // 设置解码参数
            Map<DecodeHintType, Object> hints = new HashMap<>();
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); // 强力模式
            hints.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE); // 允许有其他背景或图像
            //hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE); // 指定格式为 QR_CODE
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码
            hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(BarcodeFormat.QR_CODE));
            //hints.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE); // 如果二维码包含其他图形，设置为 false


            // 解码二维码
            //Result result = reader.decode(binaryBitmap, hints);
            return reader.decode(binaryBitmap, hints);
        } catch (Exception e) {
            try{
                Reader reader = new QRCodeReader();
                return reader.decode(binaryBitmap);

            }
            catch (Exception e1){
                return null;
            }

        }
    }

    public static BufferedImage addWhiteBorder(BufferedImage image, int borderSize) {
        int newWidth = image.getWidth() + borderSize * 2;
        int newHeight = image.getHeight() + borderSize * 2;

        BufferedImage newImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = newImage.createGraphics();

        // 填充白色背景
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, newWidth, newHeight);

        // 绘制原始图片
        graphics.drawImage(image, borderSize, borderSize, null);
        graphics.dispose();

        return newImage;
    }
    // Convert image to grayscale
    private static BufferedImage toGrayscale(BufferedImage bufferedImage) {
        ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
        return op.filter(bufferedImage, null);
    }

    // Resize image to a specific width and height
    private static BufferedImage resize(BufferedImage img, int newW, int newH) {
        Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
        BufferedImage resized = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = resized.createGraphics();
        g2d.drawImage(tmp, 0, 0, null);
        g2d.dispose();
        return resized;
    }
    public static BufferedImage convertToBlackAndWhite(BufferedImage colorImage) {
        BufferedImage bwImage = new BufferedImage(colorImage.getWidth(), colorImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
        for (int y = 0; y < colorImage.getHeight(); y++) {
            for (int x = 0; x < colorImage.getWidth(); x++) {
                int rgb = colorImage.getRGB(x, y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >> 8) & 0xFF;
                int b = rgb & 0xFF;
                int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b); // 灰度化公式
                int bwPixel = (gray < 128) ? 0 : 255; // 二值化
                int bwRgb = (bwPixel << 16) | (bwPixel << 8) | bwPixel;
                bwImage.setRGB(x, y, bwRgb);
            }
        }
        return bwImage;
    }



    private static String getBase64Image(PDDocument doc) throws IOException {
        PDFRenderer renderer = new PDFRenderer(doc);
        BufferedImage image = renderer.renderImageWithDPI(0, 150);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", byteArrayOutputStream);
        return "data:image/png;base64," + new String(Base64.encodeBase64(byteArrayOutputStream.toByteArray()));
    }

    private static void SetTotalAmountForOldInvoice(PDDocument doc, Invoice invoice) throws IOException {
        if (invoice.isElectronic()) return;
        double value = PdfAmountExtractor.extract(doc);
        if (value == 0) return;
        BigDecimal decimal = new BigDecimal(value);
        invoice.setTotalAmount(decimal);
    }
}
